package lex

import (
	"strings"

	"gitcode.com/deyiyangyang/gascheme/tok"
)

// Lexer represents a lexical scanner
type Lexer struct {
	input        string
	position     int  // current position in input (points to current char)
	readPosition int  // current reading position in input (after current char)
	ch           rune // current char under examination
	line         int  // current line number
	column       int  // current column number
}

// NewLexer creates a new lexer
func NewLexer(input string) *Lexer {
	l := &Lexer{
		input:  input,
		line:   1,
		column: 0,
	}
	l.readChar()
	return l
}

// readChar reads the next character from input
func (l *Lexer) readChar() {
	if l.readPosition >= len(l.input) {
		l.ch = 0 // EOF
	} else {
		l.ch = rune(l.input[l.readPosition])
	}
	l.position = l.readPosition
	l.readPosition++
	l.column++

	// Update line and column for newlines
	if l.ch == '\n' {
		l.line++
		l.column = 0
	}
}

// peekChar returns the next character without advancing the position
func (l *Lexer) peekChar() rune {
	if l.readPosition >= len(l.input) {
		return 0
	} else {
		return rune(l.input[l.readPosition])
	}
}

// NextToken returns the next token from the input
func (l *Lexer) NextToken() tok.Token {
	var token tok.Token

	l.skipWhitespaceAndComments()

	// Check for special characters and delimiters
	switch l.ch {
	case '(':
		token = l.newToken(tok.LPAREN, "(")
		l.readChar()
	case ')':
		token = l.newToken(tok.RPAREN, ")")
		l.readChar()
	case '\'':
		token = l.newToken(tok.QUOTE, "'")
		l.readChar()
	case '"':
		token.Type = tok.STRING
		token.Lexeme = l.readString()
		token.Line = l.line
		token.Column = l.column - len(token.Lexeme)
	case '#':
		// Handle boolean and character literals
		if l.peekChar() == 't' || l.peekChar() == 'f' {
			token = l.readBoolean()
		} else if l.peekChar() == '\\' {
			token = l.readCharLiteral()
		} else {
			token = l.newToken(tok.ILLEGAL, string(l.ch))
			l.readChar()
		}
	case 0:
		token = l.newToken(tok.EOF, "")
	default:
		// 先判断是否为负号后跟数字的情况（负数）
		if l.ch == '-' && isDigit(l.peekChar()) {
			token = l.readNumber()
		} else if isDigit(l.ch) {
			// 对于数字开头的情况，我们需要特殊处理
			// 尝试读取数字
			tempLexer := &Lexer{
				input:        l.input,
				position:     l.position,
				readPosition: l.readPosition,
				ch:           l.ch,
				line:         l.line,
				column:       l.column,
			}
			tempToken := tempLexer.readNumber()

			// 检查读取完数字后是否到达了空白字符或EOF或括号
			if tempLexer.ch == 0 || tempLexer.ch == ' ' || tempLexer.ch == '\t' || tempLexer.ch == '\n' || tempLexer.ch == '\r' || tempLexer.ch == '(' || tempLexer.ch == ')' {
				// 这是一个真正的数字字面量
				token = tempToken
				// 更新原lexer的状态
				l.position = tempLexer.position
				l.readPosition = tempLexer.readPosition
				l.ch = tempLexer.ch
				l.line = tempLexer.line
				l.column = tempLexer.column
			} else {
				// 不是纯数字，是数字开头的符号
				token = l.readIdentifier()
			}
		} else if isLetter(l.ch) || isSymbolChar(l.ch) {
			// 普通的标识符或符号
			token = l.readIdentifier()
		} else {
			token = l.newToken(tok.ILLEGAL, string(l.ch))
			l.readChar()
		}
	}

	return token
}

// skipWhitespaceAndComments skips over whitespace characters and comments
func (l *Lexer) skipWhitespaceAndComments() {
	for {
		// Skip whitespace
		switch l.ch {
		case ' ', '\t', '\n', '\r':
			l.readChar()
		// Skip comments
		case ';':
			// Skip until end of line or EOF
			for l.ch != '\n' && l.ch != 0 {
				l.readChar()
			}
		default:
			return // 当遇到非空白字符且非注释字符时，退出函数
		}
	}
}

// readIdentifier reads an identifier or symbol
func (l *Lexer) readIdentifier() tok.Token {
	position := l.position

	for isLetter(l.ch) || isDigit(l.ch) || isSymbolChar(l.ch) {
		l.readChar()
	}

	return l.newToken(tok.SYMBOL, l.input[position:l.position])
}

// readNumber reads integer, rational, and real number literals
func (l *Lexer) readNumber() tok.Token {
	position := l.position
	isRational := false
	isReal := false

	// Handle negative numbers
	if l.ch == '-' {
		l.readChar()
	}

	// Read integer part
	for isDigit(l.ch) {
		l.readChar()
	}

	// Check for rational number
	if l.ch == '/' && isDigit(l.peekChar()) {
		isRational = true
		l.readChar() // consume '/'
		// Read fractional part
		for isDigit(l.ch) {
			l.readChar()
		}
	} else if l.ch == '.' && isDigit(l.peekChar()) {
		// Check for real number
		isReal = true
		l.readChar() // consume '.'
		// Read decimal part
		for isDigit(l.ch) {
			l.readChar()
		}
	}

	tokenType := tok.INT
	if isRational {
		tokenType = tok.RATIONAL
	} else if isReal {
		tokenType = tok.REAL
	}

	return l.newToken(tokenType, l.input[position:l.position])
}

// readString reads a string literal enclosed in double quotes
// 返回不带引号的字符串值（包括转义字符）
func (l *Lexer) readString() string {
	l.readChar() // consume the opening quote

	var value strings.Builder
	for l.ch != '"' && l.ch != 0 {
		if l.ch == '\\' {
			// 保存反斜杠
			value.WriteRune(l.ch)
			l.readChar()
			// 保存转义字符
			if l.ch != 0 {
				value.WriteRune(l.ch)
				l.readChar()
			}
		} else {
			// 保存普通字符
			value.WriteRune(l.ch)
			l.readChar()
		}
	}

	// 确保包含结束引号
	if l.ch == '"' {
		l.readChar() // consume the closing quote
	}

	// 返回不带引号的字符串值
	return value.String()
}

// readBoolean reads a boolean literal (#t or #f)
func (l *Lexer) readBoolean() tok.Token {
	position := l.position
	l.readChar() // consume '#'
	l.readChar() // consume 't' or 'f'

	return l.newToken(tok.BOOLEAN, l.input[position:l.position])
}

// readCharLiteral reads a character literal (#\ followed by a character)
func (l *Lexer) readCharLiteral() tok.Token {
	position := l.position
	l.readChar() // consume '#'
	l.readChar() // consume '\'
	
	// Special case: if the next character is a space, we have a space character
	if l.ch == ' ' {
		l.readChar() // consume the space
		return l.newToken(tok.CHAR, l.input[position:l.position])
	}
	
	// Check for special character names like "space", "newline"
	// We need to read the following characters to determine if it's a special name
	startPos := l.position
	
	// Read characters until we hit a delimiter or whitespace
	for l.ch != ' ' && l.ch != '\t' && l.ch != '\n' && l.ch != '\r' && 
		l.ch != '(' && l.ch != ')' && l.ch != 0 && l.ch != '\'' {
		l.readChar()
	}
	
	// Get the character name
	charName := l.input[startPos:l.position]
	
	// If it's a single character or a known special name, it's valid
	if len(charName) == 1 || charName == "space" || charName == "newline" || charName == "tab" {
		return l.newToken(tok.CHAR, l.input[position:l.position])
	}
	
	// Otherwise, it's illegal
	return l.newToken(tok.ILLEGAL, l.input[position:l.position])
}

// newToken creates a new token with the given type and literal
func (l *Lexer) newToken(tokenType tok.TokenType, literal string) tok.Token {
	return tok.Token{
		Type:   tokenType,
		Lexeme: literal,
		Line:   l.line,
		Column: l.column - len(literal),
	}
}

// isLetter checks if a character is a letter
func isLetter(ch rune) bool {
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z'
}

// isDigit checks if a character is a digit
func isDigit(ch rune) bool {
	return '0' <= ch && ch <= '9'
}

// isSymbolChar checks if a character is a valid symbol character
func isSymbolChar(ch rune) bool {
	return ch == '+' || ch == '-' || ch == '!' || ch == '$' || ch == '%' || ch == '&' ||
		ch == '*' || ch == '/' || ch == ':' || ch == '<' ||
		ch == '=' || ch == '>' || ch == '?' || ch == '~' ||
		ch == '_' || ch == '^' || ch == '@' || ch == '#'
}
